import path from "path";
import fs from "fs-extra";

class BuildObj {
  buildMain() {
    // 由于 Vite 在编译之前会清空 dist 目录，所以我们在上一节中生成的 mainEntry.js 文件也被删除了，此处我们通过buildMain方法再次编译主进程的代码。不过由于此处是在为生产环境编译代码，所以我们增加了minify: true 配置，生成压缩后的代码。如果你希望与开发环境复用编译主进程的代码，也可以把这部分代码抽象成一个独立的方法
    require("esbuild").buildSync({
      entryPoints: ["./src/main/mainEntry.ts"],
      bundle: true,
      platform: "node",
      minify: true,
      outfile: "./dist/mainEntry.js",
      external: ["electron"],
    });
  }
  prepareKnexjs() {
    let pkgJsonPath = path.join(process.cwd(), `dist/node_modules/knex`);
    fs.ensureDirSync(pkgJsonPath);
    require("esbuild").buildSync({
      entryPoints: ["./node_modules/knex/knex.js"],
      bundle: true,
      platform: "node",
      format: "cjs",
      minify: true,
      outfile: "./dist/node_modules/knex/index.js",
      external: ["oracledb", "pg-query-stream", "pg", "sqlite3", "tedious", "mysql", "mysql2", "better-sqlite3"],
      // external是为了避免编译过程中esbuild去寻找这些模块而导致编译失败，也就是说Knex.js中这样的代码会保持原样输出到编译产物中：require('better-sqlite3')
    });
    let pkgJson = `{"name": "bindings","main": "index.js"}`;
    pkgJsonPath = path.join(process.cwd(), `dist/node_modules/knex/package.json`);
    fs.writeFileSync(pkgJsonPath, pkgJson);
  }
  async prepareSqlite() {
    //拷贝better-sqlite3
    // 把开发环境的node_modules\better-sqlite3目录下有用的文件拷贝到dist\node_modules\better-sqlite3目录下，并为这个模块自制了一个简单的package.json
    let srcDir = path.join(process.cwd(), `node_modules/better-sqlite3`);
    let destDir = path.join(process.cwd(), `dist/node_modules/better-sqlite3`);
    fs.ensureDirSync(destDir);
    fs.copySync(srcDir, destDir, {
      filter: (src, dest) => {
        if (src.endsWith("better-sqlite3") || src.endsWith("build") || src.endsWith("Release") || src.endsWith("better_sqlite3.node")) return true;
        else if (src.includes("node_modules\\better-sqlite3\\lib")) return true;
        else return false;
      },
    });

    let pkgJson = `{"name": "better-sqlite3","main": "lib/index.js"}`;
    let pkgJsonPath = path.join(process.cwd(), `dist/node_modules/better-sqlite3/package.json`);
    fs.writeFileSync(pkgJsonPath, pkgJson);

    //制作bindings模块
    // 完全自己制作了一个bindings模块，把这个模块放置在dist\node_modules\bindings目录下
    let bindingPath = path.join(process.cwd(), `dist/node_modules/bindings/index.js`);
    fs.ensureFileSync(bindingPath);
    let bindingsContent = `module.exports = () => {
  let addonPath = require("path").join(__dirname, '../better-sqlite3/build/Release/better_sqlite3.node');
  return require(addonPath);
  };`;
    fs.writeFileSync(bindingPath, bindingsContent);

    pkgJson = `{"name": "bindings","main": "index.js"}`;
    pkgJsonPath = path.join(process.cwd(), `dist/node_modules/bindings/package.json`);
    fs.writeFileSync(pkgJsonPath, pkgJson);
    }
  preparePackageJson() {
    // 用户安装我们的产品后，在启动我们的应用程序时，实际上是通过 Electron 启动一个 Node.js 的项目，所以我们要为这个项目准备一个 package.json 文件，这个文件是以当前项目的 package.json 文件为蓝本制作而成的。里面注明了主进程的入口文件，移除了一些对最终用户没用的配置节
    let pkgJsonPath = path.join(process.cwd(), "package.json");
    let localPkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
    //https://github.com/electron-userland/electron-builder/issues/4157#issuecomment-596419610
    let electronConfig = localPkgJson.devDependencies.electron.replace("^", "");
    // 这段脚本还明确指定了 Electron 的版本号，如果 Electron 的版本号前面有"^"符号的话，需把它删掉。这是 electron-builder 的一个 Bug，这个 bug 导致 electron-builder 无法识别带 ^ 或 ~ 符号的版本号。
    localPkgJson.main = "mainEntry.js";
    delete localPkgJson.scripts;
    delete localPkgJson.devDependencies;
    localPkgJson.devDependencies = { electron: electronConfig };
    // 避免 electron-builder 为我们安装better-sqlite3/bindings/Knex.js模块
    localPkgJson.dependencies["better-sqlite3"] = "*";
    localPkgJson.dependencies["bindings"] = "*";
    localPkgJson.dependencies["knex"] = "*";
    let tarJsonPath = path.join(process.cwd(), "dist", "package.json");
    fs.writeFileSync(tarJsonPath, JSON.stringify(localPkgJson));
    fs.mkdirSync(path.join(process.cwd(), "dist/node_modules"));
  }
  buildInstaller() {
    let options = {
      config: {
        extraResources: [{ from: `./src/common/db.db`, to: `./` }],
        // 通过 electron-builder 的 extraResources 配置完成把数据库文件放置在终端用户的数据目录下
        // extraResources，可以让开发者为安装包指定额外的资源文件，electron-builder 打包应用时会把这些资源文件附加到安装包内，当用户安装应用程序时，这些资源会释放在安装目录的 resources\子目录下。
        directories: {
          output: path.join(process.cwd(), "release"), // 最终生成的安装包目录
          app: path.join(process.cwd(), "dist"), //静态文件所在目录
        },
        files: ["**"],
        extends: null,
        productName: "Lbxin",
        appId: "com.Lbxin.desktop",
        asar: true, // 把输出目录下的文件合并成一个 asar 文件
        nsis: {
          oneClick: true,
          perMachine: true,
          allowToChangeInstallationDirectory: false,
          createDesktopShortcut: true,
          createStartMenuShortcut: true,
          shortcutName: "LbxinDesktop",
        },
        publish: [{ provider: "generic", url: "http://localhost:5500/" }],
      },
      project: process.cwd(),
    };
    return require("electron-builder").build(options);
  }
}

export let buildPlugin = () => {
    return {
        name: 'build-plugin',
        closeBundle: () => {
            let buildObj = new BuildObj();
            buildObj.buildMain();
            buildObj.preparePackageJson();
            buildObj.prepareSqlite()
            buildObj.prepareKnexjs();
            buildObj.buildInstaller();
        }
    }
}